import Quill from "quill";
import { Delta } from "quill/core";
import "quill/dist/quill.snow.css";
import ColorThief from "colorthief";
import { Draw } from "../Draw/Draw";
import { toBlob } from "html-to-image";
import { getColorMatch } from "../Utils";
import { IKonva } from "../Interface/IKonva";
import { MathfieldElement } from "mathlive";
import { convertLatexToMarkup } from "mathlive";
import { LATEX_MAP } from "../Config/latex-map";
import { IDialogInfo } from "../Interface/Pptx";
import { SLIDE_TEMPLATE } from "../Config/slide-template";
import { color_hexToHsl, color_hexToRGB } from "../Utils/konva";
import { color_hslToHex, color_rgbToHex } from "../Utils/konva";
import { setStageCursor } from "../Event/handle/konva/stage-cursor";
import { COLOR_GEGERATE_ARRAY } from "../Config/color-generate-array";
import { ColorMatchType, IColorMatch } from "../Interface/ColorMatch";
import { customColorSelectTemp, getComBox } from "../Template/DialogTemp";
import { customColorImgTemp, customColorTemp } from "../Template/DialogTemp";

export class Dialog {
  private draw: Draw;
  private payload!: IDialogInfo;
  private quill: Quill | null = null;
  private confirmHandle!: () => Promise<void>;
  private dialog: HTMLDivElement | null = null;
  private result: object | Blob | null | IColorMatch = null;

  constructor(draw: Draw) {
    this.draw = draw;
  }

  /** 打开弹窗 */
  public open(payload: IDialogInfo) {
    const { content, title, width = 580, height = 360 } = payload;
    this.payload = payload;
    this.dialog = document.createElement("div");
    this.dialog.className = "konva-root-dialog";
    this.dialog.innerHTML = getComBox(width, height, content, title);
    document.body.appendChild(this.dialog);

    // 初始化默认事件 - 按钮关闭、确认关闭
    this.addDefaultEvent(payload);

    // 初始化内容区事件
    this.initContentEvent(payload.content);

    // 如果 data 存在，则需要调用响应方法，设置值，因为 dialog 内部的对象，外部访问不到，因此需要这样操作
    if (payload.delta) this.setDelta(payload.delta);
  }

  /**
   * 处理默认事件 - 关闭、确认、取消、蒙版事件等
   */
  private addDefaultEvent(payload: IDialogInfo) {
    if (!this.dialog) return;

    // 内容区点击要阻止冒泡
    const contentSelector = ".konva-root-dialog-content";
    const content = this.dialog.querySelector(contentSelector);
    content?.addEventListener("click", (e) => e.stopPropagation());

    // 蒙版点击直接关闭
    // this.dialog.addEventListener("click", () => this.close(payload));

    // &times; 关闭按钮
    const timesSelector = ".konva-root-dialog-header .close";
    const times = this.dialog.querySelector(timesSelector);
    times?.addEventListener("click", () => this.close(payload));

    // 关闭按钮
    const closeSelector = ".konva-root-dialog-footer .close";
    const close = this.dialog.querySelector(closeSelector);
    close?.addEventListener("click", () => this.close(payload));

    // 确认按钮
    const confirmSelector = ".konva-root-dialog-footer .confirm";
    const confirm = this.dialog.querySelector(confirmSelector);
    confirm?.addEventListener("click", () => this.close(payload, true));
  }

  private async close(payload: IDialogInfo, confirm?: boolean) {
    if (!this.dialog) return;

    // 如果是确认按钮，先等待结果，再关闭弹窗
    if (confirm && this.confirmHandle) await this.confirmHandle();

    this.dialog.remove();

    const { onCancel, onClose, onConfirm } = payload;
    if (onCancel) onCancel();
    if (onClose) onClose();
    if (onConfirm) onConfirm(this.result);

    // 从这里统一设置 stage 样式
    setStageCursor(this.draw, "default");
  }

  /**
   * 处理弹窗内容事件 - 表格输入、统计图配置等
   */
  private initContentEvent(content: string) {
    // 识别表格
    if (content.includes("konva-root-dialog-table")) this.initTableEvent();

    // 识别统计图
    if (content.includes("konva-root-dialog-echart")) this.initEchartEvent();

    // 识别公式
    if (content.includes("konva-root-dialog-latex")) this.initLatexEvent();

    // 识别富文本
    if (content.includes("richtext-editor")) this.initRichTextEvent();

    // 识别自定义配色
    if (content.includes("color-match-container")) this.initColorMatchEvent();

    // 识别幻灯片模板
    if (content.includes("slide-template")) this.initSlideTempEvent();
  }

  // 初始化表格事件
  private initTableEvent() {
    if (!this.dialog) return;

    const tableSelector = ".konva-root-dialog-table";
    const table = this.dialog.querySelector(tableSelector);

    // 初始化 - 获取当前配色方案
    const table_headerColor = getColorMatch(this.draw, "tableHeaderFillColor");
    const table_stripeColor = getColorMatch(this.draw, "tableStripeColor");

    // 初始化 - 设置表头颜色
    const header = <HTMLElement>table?.querySelector("thead tr");
    header.style.backgroundColor = <string>table_headerColor;

    // 初始化 - 实现斑马纹效果
    type NLHTL = NodeListOf<HTMLTableElement>;
    const trs = <NLHTL>table?.querySelectorAll("tbody tr");
    for (let i = 0; i < trs.length; i++) {
      if (i % 2) trs[i].style.backgroundColor = <string>table_stripeColor?.[0];
      else trs[i].style.backgroundColor = <string>table_stripeColor?.[1];
    }

    // 1. 识别行列输入框，动态生成表格
    type HIE = HTMLInputElement;
    const rows = <HIE>table?.querySelector("#konva-root-table-rows");
    const cols = <HIE>table?.querySelector("#konva-root-table-cols");

    // 监听行列输入框的变化，动态生成表格
    rows?.addEventListener("input", rowsChangeHandle);
    cols?.addEventListener("input", colsChangeHandle);

    /**
     * 行变化
     */
    function rowsChangeHandle() {
      const rowsValue = Number(rows?.value);
      // 获取当前行数
      const currentRows = table?.querySelectorAll("tbody tr")?.length || 0;
      if (rowsValue === currentRows) return;

      // 加行
      if (rowsValue > currentRows) {
        // 需要增加行
        for (let i = 0; i < rowsValue - currentRows; i++) {
          // 获取当前列数
          const currentCols =
            table?.querySelectorAll("thead tr th").length || 0;
          let newRow = ``;
          for (let j = 0; j < currentCols; j++) {
            newRow += `<td><input /></td>`;
          }

          table
            ?.querySelector("tbody")
            ?.insertAdjacentHTML("beforeend", `<tr>${newRow}</tr>`);
        }
      }

      // 减行
      if (rowsValue < currentRows) {
        // 需要删除行 - 从末尾删除
        for (let i = 0; i < currentRows - rowsValue; i++) {
          table?.querySelector("tbody")?.lastElementChild?.remove();
        }
      }
    }

    /**
     * 列变化
     */
    function colsChangeHandle() {
      const colsValue = Number(cols?.value);
      // 列增加的话， thead 需要增加 th 因此需要判断 cols 是否增加
      // 获取当前列数
      const currentCols = table?.querySelectorAll("thead tr th").length || 0;
      if (currentCols === colsValue) return;

      // 增加列
      if (colsValue > currentCols) {
        // 需要增加列
        for (let i = 0; i < colsValue - currentCols; i++) {
          const index = i * colsValue + currentCols + 1;
          const newRow = `<th><input value="标题${index}" /></th>`;
          table
            ?.querySelector("thead tr")
            ?.insertAdjacentHTML("beforeend", newRow);

          // body 也要同步添加
          table
            ?.querySelectorAll("tbody tr")
            .forEach((tr) =>
              tr.insertAdjacentHTML("beforeend", `<td><input /></td>`)
            );
        }
      }

      // 删除列
      if (colsValue < currentCols) {
        // 删除 thead 最后一项
        table?.querySelector("thead tr")?.lastElementChild?.remove();
        // 删除 tbodt 最后一项
        table
          ?.querySelectorAll("tbody tr")
          .forEach((tr) => tr.lastElementChild?.remove());
      }
    }

    // 确认按钮点击时，进行数据整理
    this.confirmHandle = () => {
      // 表格二维数组
      const data: string[][] = [];
      // 获取表头
      type NLOI = NodeListOf<HTMLInputElement>;
      const theadInputs = <NLOI>table?.querySelectorAll("thead input");
      theadInputs?.forEach((input) => {
        if (!data[0]) data[0] = [];
        data[0].push(input.value);
      });

      // 获取内容体
      type NLOT = NodeListOf<HTMLTableElement>;
      const tbodyTrs = <NLOT>table?.querySelectorAll("tbody tr");
      tbodyTrs.forEach((tr, index) => {
        if (!data[index + 1]) data[index + 1] = [];
        tr.querySelectorAll("input").forEach((input) => {
          data[index + 1].push(input.value);
        });
      });

      this.result = { data };
      return Promise.resolve();
    };
  }

  // 初始化统计图事件
  private initEchartEvent() {
    if (!this.dialog) return;

    // 支持统计图类型：折线图、柱状图、饼图、雷达图
    type HIE = HTMLInputElement;
    const categoryInput = <HIE>this.dialog.querySelector("#category");
    const seriesInput = <HIE>this.dialog.querySelector("#series");
    const select = <HTMLSelectElement>this.dialog.querySelector("select");
    const echart = this.dialog.querySelector(".konva-root-dialog-echart")!;
    const titleInput = <HTMLInputElement>this.dialog.querySelector("#title");
    const message = this.draw.getMessage();

    categoryInput?.addEventListener("input", categoryChangeHandle);
    seriesInput?.addEventListener("input", seriesChangeHandle);
    select.addEventListener("change", selectChangeHandle);

    /**
     * 类别变化函数
     */
    function categoryChangeHandle() {
      const categoryValue = Number(categoryInput?.value);
      if (categoryValue < 1) {
        categoryInput.value = "1";
        message.warn("类别数量不能小于1");
        return;
      }
      // 获取当前的类别数量
      const currentCategory = echart.querySelectorAll("tbody tr")?.length || 0;
      if (categoryValue === currentCategory) return;

      // 加类别
      if (categoryValue > currentCategory) {
        // 需要增加行
        for (let i = 0; i < categoryValue - currentCategory; i++) {
          // 获取当前列数
          const currentCols =
            echart.querySelectorAll("thead tr th").length || 0;
          let newRow = ``;
          for (let j = 0; j < currentCols; j++) {
            newRow += `<td><input data-check="false" ${
              !j && `value='类别${currentCategory + j + 1}'`
            } /></td>`;
          }

          echart
            ?.querySelector("tbody")
            ?.insertAdjacentHTML("beforeend", `<tr>${newRow}</tr>`);
        }
      }

      // 减少类别
      if (categoryValue < currentCategory) {
        // 需要删除行 - 从末尾删除
        for (let i = 0; i < currentCategory - categoryValue; i++) {
          echart.querySelector("tbody")?.lastElementChild?.remove();
        }
      }

      // 更新事件
      addInputEvent();
      // 更新值
      inputChangeHandle();
    }

    /**
     * 系列变化函数
     */
    function seriesChangeHandle() {
      const seriesValue = Number(seriesInput?.value);
      if (seriesValue < 1) {
        seriesInput.value = "1";
        message.warn("系列数量不能小于1");
        return;
      }
      if (seriesValue > 1 && select.value === "pie")
        message.warn("多系列对比，建议使用其他图形！");

      const currentSeries =
        echart.querySelectorAll("thead tr th")?.length - 1 || 0;
      if (seriesValue === currentSeries) return;

      if (seriesValue > currentSeries) {
        // 需要增加列
        for (let i = 0; i < seriesValue - currentSeries; i++) {
          const index = i * seriesValue + currentSeries + 1;
          const newRow = `<th><input data-check="false" value="系列${index}" /></th>`;
          echart
            .querySelector("thead tr")
            ?.insertAdjacentHTML("beforeend", newRow);

          // body 也要同步添加
          echart
            .querySelectorAll("tbody tr")
            .forEach((tr) =>
              tr.insertAdjacentHTML("beforeend", `<td><input /></td>`)
            );
        }
      }

      if (seriesValue < currentSeries) {
        // 需要删除列 - 从末尾删除
        for (let i = 0; i < currentSeries - seriesValue; i++) {
          echart.querySelector("thead tr")?.lastElementChild?.remove();
          echart
            .querySelectorAll("tbody tr")
            .forEach((tr) => tr.lastElementChild?.remove());
        }
      }

      // 更新事件
      addInputEvent();
      // 更新值
      inputChangeHandle();
    }

    // 选择框变化事件
    function selectChangeHandle() {
      // 判断系列数是否大于1
      const seriesValue = Number(seriesInput?.value);
      if (seriesValue > 1 && select.value === "pie")
        message.warn("多系列对比，建议使用其他图形！");
    }

    // 检测数值型
    function isNumber(value: string) {
      return !isNaN(Number(value));
    }

    // 添加事件
    function addInputEvent() {
      // table 下面的输入框 - 包括 thead 和 tbody ，内容变化后，需要实时更新 table 二维表格，供 确认按钮 返回
      const tableInputs = echart.querySelectorAll("thead input,tbody input");
      tableInputs?.forEach((input) => {
        input.removeEventListener("input", inputChangeHandle);
        input.addEventListener("input", inputChangeHandle);
      });
    }

    // 输入框变化事件
    const inputChangeHandle = (e?: Event) => {
      if (e && !(<HIE>e?.target).dataset?.check) {
        // 1. 检查数值型
        const currentValue = (<HIE>e?.target)?.value;
        if (!isNumber(currentValue)) {
          message.error(`值\`${currentValue}\`非法，请输入数值型!`);
          (<HIE>e.target).value = currentValue.replace(
            (<InputEvent>e).data!,
            ""
          );
        }
      }
    };

    addInputEvent();

    this.confirmHandle = () => {
      const xAxis = <string[]>[];
      const series = <IKonva.EchartSeries[]>[];
      // 2. 获取类别 ,是 tbody 的tr 第一个 td
      echart.querySelectorAll("tbody tr").forEach((tr) => {
        xAxis.push(<string>tr.firstElementChild?.querySelector("input")!.value);
      });

      // 3. 获取系列 - {name:xxx,data:[]}
      echart.querySelectorAll("thead tr th").forEach((th, index) => {
        if (!index) return; // 第一列没有任何含义
        const data = { name: "", data: <number[]>[] };
        data.name = <string>th.querySelector("input")!.value;
        // 解析 data - 根据 index 去 body 中 找对应的input 即可
        echart.querySelectorAll("tbody tr").forEach((tr) => {
          const input = <HIE>tr.children[index]?.querySelector("input");
          data.data.push(Number(input.value));
        });

        series.push(data);
      });

      this.result = {
        echartTitle: <string>titleInput.value,
        echartType: <IKonva.EchartType>select.value,
        xAxis,
        series,
      };

      return Promise.resolve();
    };
  }

  /**
   * 实现原理：
   *  页面展示 svg 预览，绑定唯一的latex command；
   *  点击时，通过 latex.executeCommand("insert", command) 进行插入
   *  如果遇到公式无法正正常显示问题，应该是字体文件 没有加载，需要手动加载
   *  加载方法为 https://cortexjs.io/mathlive/demo/ network
   *            node_modules/mathlive/fonts/xxx
   *  请将字体文件放在 public fonts 文件夹下
   *  打包lib字体应该不会被加载，需要额外提供字体文件
   */
  private initLatexEvent() {
    if (!this.dialog) return;
    const latex = new MathfieldElement();
    // 先初始化 latex 编辑器
    const latexBox = this.dialog.querySelector(".mathlive");
    latexBox?.appendChild(latex);

    // 2. 初始化菜单列表
    const menuSelector = ".konva-root-dialog-latex-menu span";
    const latexMenus = this.dialog.querySelectorAll(menuSelector)!;
    for (let i = 0; i < latexMenus.length; i++) {
      latexMenus[i].addEventListener("click", () => {
        // 1. 先移除所有选中状态
        latexMenus.forEach((item) => item.classList.remove("active"));
        // 2. 设置当前选中状态
        latexMenus[i].classList.add("active");
        // 3. 设置对应的菜单
        setActiveMenu(i);
      });
    }

    // 3. 设置对象菜单
    const menuItemSelector = ".konva-root-dialog-latex-items";
    const menuItem = this.dialog.querySelector(menuItemSelector)!;
    function setActiveMenu(index: number) {
      // 获取对应的 菜单项
      const latexItems = LATEX_MAP[index];
      menuItem.innerHTML = "";
      for (const svg in latexItems) {
        if (Object.prototype.hasOwnProperty.call(latexItems, svg)) {
          const command = latexItems[svg];
          // 设置 svg 的属性
          const newsvg = svg.replace("<svg", `<svg data-command="${command}"`);
          menuItem.innerHTML += newsvg;
        }
      }

      // 重新设置 svg 的点击事件
      setSvgEvent();
    }

    // 4. 默认初始化常用菜单项
    setActiveMenu(0);

    // 重新设置 svg 的点击事件
    function setSvgEvent() {
      const svgs = menuItem.querySelectorAll("svg");
      svgs.forEach((svg) => {
        svg.addEventListener("click", () => {
          // 获取对应的 latex 命令
          const command = <string>svg.dataset?.command;
          latex.executeCommand("insert", command);
          latex.focus();
        });
      });
    }

    this.confirmHandle = () => {
      return new Promise<void>((resolve) => {
        if (!latex.value) resolve();

        const loading = this.draw.getLoading();
        loading.show("正在生成公式...");

        // 1. 将公式转为 HTML
        const html = convertLatexToMarkup(latex.value);

        // 2. 添加到页面
        const htmlBox = document.createElement("div");
        htmlBox.style.position = "absolute";
        htmlBox.style.zIndex = "-1";
        htmlBox.style.top = "0";
        htmlBox.style.padding = "10px";
        htmlBox.style.backgroundColor = "transparent";
        htmlBox.innerHTML = html;

        // 将 html 添加到dialog  body 下，并且不影响页面显示
        const bodySelector = ".konva-root-dialog-body";
        const dialog_body = this.dialog!.querySelector(bodySelector)!;
        dialog_body.appendChild(htmlBox);

        // 3. 使用 html-to-image 转成 blob
        toBlob(htmlBox).then((blob) => {
          this.result = blob;
          loading.hide();
          resolve();
        });
      });
    };
  }

  /**
   * Quill 富文本编辑器:
   * https://quilljs.com/docs/quickstart
   */
  private initRichTextEvent() {
    const _query = (selector: string) => this.dialog?.querySelector(selector);

    // 修正 dialog 高度问题，避免出现滚动条导致 html-to-image 导出图片不完整
    type HE = HTMLElement;
    const content = <HE>_query(".konva-root-dialog-content");
    content.style.minHeight = content.style.height;
    content.style.height = "auto";

    // 1. 初始化 quill
    const quill = new Quill(".richtext-editor #editor", {
      placeholder: "input your content...",
      modules: {
        toolbar: "#toolbar-container",
      },
      theme: "snow",
    });
    this.quill = quill;

    // 2. 确认按钮转 blob
    this.confirmHandle = () => {
      return new Promise<void>((resolve) => {
        // 为了下次编辑,还需要将当前的 Delta 转存到 shape 中
        // 下次编辑 setContents(delta: Delta): Delta
        let delta: Delta | null = null;
        delta = quill.getContents();

        // 判断 delta 是否合法
        const length = quill.getLength();
        if (length <= 1) delta = null;

        // **获取文本宽度高度，以达到最佳的展示效果，实现图片的裁剪
        quill.root.parentElement!.style.display = "flex";

        toBlob(quill.root).then((source) => {
          this.result = { source, delta };
          resolve();
        });
      });
    };
  }

  /**
   * 富文本双击重新编辑，需要将旧的 delta 转换成 quill 的内容
   * @param delta
   */
  private setDelta(delta: Delta) {
    if (!this.quill) return;
    this.quill.setContents(delta);
  }

  // 初始化自定义配色事件
  private initColorMatchEvent() {
    if (!this.dialog) return;
    // 设置菜单结构
    const contentBox = this.dialog.querySelector(".content-box")!;
    const contentMap = [
      customColorTemp,
      customColorImgTemp,
      customColorSelectTemp,
    ];
    const contentEventMap = [
      this.initCustomColorEvent,
      this.initImageGenerateEvent,
      this.initColorGenerateEvent,
    ];

    const setMenuContent = (index: number) => {
      contentBox.innerHTML = contentMap[index];
      contentEventMap[index].call(this);
    };

    // 先初始化一下自定义颜色菜单
    setMenuContent(0);
    this.renderColorMatch();

    // 1. 获取自定义配色的按钮
    const colorMatchBox = this.dialog.querySelector(".color-match-container");
    const menuList = colorMatchBox?.querySelectorAll(".menu span");
    menuList?.forEach((item, index) => {
      item.addEventListener("click", () => {
        // 1. 先移除所有选中状态
        menuList.forEach((item) => item.classList.remove("active"));
        // 2. 设置当前选中状态
        item.classList.add("active");
        // 3. 设置对应的菜单
        setMenuContent(index);
      });
    });
  }

  // 初始化 自定义颜色 菜单事件
  private initCustomColorEvent() {
    if (!this.dialog) return;

    const contentBox = this.dialog.querySelector(".content-box")!;
    // 这里主要是先初始化各个颜色选择框,与当前配色一致,才做选择
    const defaultTheme = this.draw.getColorScheme().getColorMatch();

    type NOFHIE = NodeListOf<HTMLInputElement>;
    const inputs = <NOFHIE>contentBox.querySelectorAll("input");

    const tableStripeColor = ["tableStripeColor1", "tableStripeColor2"];
    const seriesColor = [
      "seriesColor1",
      "seriesColor2",
      "seriesColor3",
      "seriesColor4",
      "seriesColor5",
    ];

    function getInputsValue() {
      // eslint-disable-next-line
      // @ts-ignore
      const inputsArray = [...inputs];
      const result: IColorMatch = {
        tableStripeColor: [],
        seriesColor: [],
      };
      inputsArray.forEach((input) => {
        const type = <keyof IColorMatch>input.dataset.colormatchinput!;

        if (tableStripeColor.includes(type)) {
          const index = tableStripeColor.indexOf(type);
          result.tableStripeColor![index] = input.value;
        }
        if (seriesColor.includes(type)) {
          const index = seriesColor.indexOf(type);
          result.seriesColor![index] = input.value;
        }
        if (type !== "tableStripeColor" && type !== "seriesColor") {
          result[type] = input.value;
        }
      });

      return result;
    }

    // 先初始化默认配色方案
    inputs.forEach((input) => {
      const colorMatchType = input.dataset.colormatchinput!;

      if (tableStripeColor.includes(colorMatchType)) {
        const colors = defaultTheme.tableStripeColor!;
        const index = tableStripeColor.findIndex((i) => i === colorMatchType);
        input.value = colors[index];
      } else if (seriesColor.includes(colorMatchType)) {
        const colors = defaultTheme.seriesColor!;
        const index = seriesColor.findIndex((i) => i === colorMatchType);
        input.value = colors[index];
      } else {
        const value = getColorMatch(this.draw, <ColorMatchType>colorMatchType);
        // 解决 transparent 无法显示问题
        input.value = <string>(value === "transparent" ? "#ffffff" : value);
      }

      // 添加change 事件 更新预览 并且保存数据
      input.addEventListener("change", () => {
        // 这里还是要读取所有 input 的颜色，生成全部的 colorMatch
        this.result = Object.assign({}, defaultTheme, getInputsValue());
        // 如果已经提前设置了 result 的值，则此处直接.resolve() 即可
        this.renderColorMatch();
      });
    });

    this.result = Object.assign({}, defaultTheme, getInputsValue());
    this.confirmHandle = () => Promise.resolve();
  }

  // 初始化 图片生成 菜单事件
  private initImageGenerateEvent() {
    if (!this.dialog) return;

    const contentBox = this.dialog.querySelector(".content-box")!;
    const picker = contentBox.querySelector(".image-picker-box")!;
    const defaultTheme = this.draw.getColorScheme().getColorMatch();

    // 对图片主要颜色进行识别
    const getImageColor = (image: HTMLImageElement) => {
      const colorThief = new ColorThief();
      const result: IColorMatch = {
        tableStripeColor: [],
        seriesColor: [],
      };
      image.addEventListener("load", () => {
        const seriesColor = <number[][]>colorThief.getPalette(image, 10);
        // [ [r,g,b], [r,g,b], ... ] 一共生成 11 个数组 ==> 转换成 HEX
        seriesColor.forEach(([r, g, b], index) => {
          // 将该颜色转成 HEX
          const hex = color_rgbToHex(r, g, b);
          if (index === 0) {
            // 节点填充颜色
            result.nodeFillColor = hex;
            // 节点文本颜色 - 填充的互补色
            result.nodeTextColor = color_rgbToHex(255 - r, 255 - g, 255 - b);
          }
          // 形变节点颜色
          if (index === 1) result.transformerColor = hex;
          // 表头背景色
          if (index === 2) result.tableHeaderFillColor = hex;
          // 表格斑马纹 ==> 生成两个
          if (index === 3) result.tableStripeColor![0] = hex;
          if (index === 4) result.tableStripeColor![1] = hex;
          // 统计图系列色 ==> 生成5个
          result.seriesColor![index - 5] = hex;
        });
        // 拿到系类色后，生成色系
        this.result = Object.assign(defaultTheme, result);
        this.confirmHandle = () => Promise.resolve();
        // 渲染预览
        this.renderColorMatch();
      });
    };

    // 图片选择回调
    const pickerHandle = (e: Event) => {
      const input = <HTMLInputElement>e.target;
      const file = input.files![0];
      const reader = new FileReader();
      reader.readAsDataURL(file);
      reader.onload = () => {
        const image = new Image();
        image.src = <string>reader.result;
        picker.innerHTML = "";
        picker.appendChild(image);
        input.remove();
        // 进行识别
        getImageColor(image);
      };
    };

    // 取图片选择框
    picker?.addEventListener("click", () => {
      // 创建 input
      const input = document.createElement("input");
      input.type = "file";
      input.accept = "image/*";
      input.addEventListener("change", pickerHandle);
      input.click();
    });
  }

  // 初始化 选色生成 菜单事件
  private initColorGenerateEvent() {
    if (!this.dialog) return;

    const contentBox = this.dialog.querySelector(".content-box")!;
    const defaultTheme = this.draw.getColorScheme().getColorMatch();

    // 1. 内置色块
    const colorBox = contentBox.querySelector(".color-block")!;
    COLOR_GEGERATE_ARRAY.forEach((color) => {
      const colorItem = document.createElement("div");
      colorItem.classList.add("color-item");
      colorItem.style.backgroundColor = color;
      colorItem.addEventListener("click", () => colorItemClick(color));
      colorBox.appendChild(colorItem);
    });

    // 再创建一个 input 颜色选择框
    const colorPicker = document.createElement("div");
    const colorInput = document.createElement("input");
    colorInput.type = "color";
    colorInput.addEventListener("change", () =>
      colorItemClick(colorInput.value)
    );
    colorPicker.appendChild(colorInput);
    colorBox.appendChild(colorPicker);

    const result: IColorMatch = {
      tableStripeColor: [],
      seriesColor: [],
    };

    /** 一共需要生成 10 个颜色，主色 + （文本颜色）互补色 + 8个正互补色 */
    function getComplementaryColors(color: string) {
      const colorList = [color];

      const { r, g, b } = color_hexToRGB(color);
      colorList.push(color_rgbToHex(255 - r, 255 - g, 255 - b));

      // 将颜色转换为HSL格式
      const { h: hue, s, l } = color_hexToHsl(color);

      // 计算互补色的色调
      for (let i = 0; i < 10; i++) {
        const chue = (hue + (360 / 10) * i) % 360;
        colorList.push(color_hslToHex(chue, s, l));
      }
      return colorList;
    }

    const colorItemClick = (color: string) => {
      const colorList = getComplementaryColors(color);
      colorList.forEach((color, index) => {
        if (index === 0) result.nodeFillColor = color;
        if (index === 1) result.nodeTextColor = color;
        if (index === 3) result.tableHeaderFillColor = color;
        if (index === 4) result.tableStripeColor![0] = color;
        if (index === 5) result.tableStripeColor![1] = color;
        if (index === 6) result.seriesColor![0] = color;
        if (index === 7) result.seriesColor![1] = color;
        if (index === 8) result.seriesColor![2] = color;
        if (index === 9) result.seriesColor![3] = color;
        if (index === 10) result.seriesColor![4] = color;
        if (index === 11) result.transformerColor = color;
      });

      this.result = Object.assign(defaultTheme, result);
      this.renderColorMatch();
      this.confirmHandle = () => Promise.resolve();
    };
  }

  // 自定义配色预览实现
  private renderColorMatch() {
    if (!this.dialog) return;

    /**
     * 绘制的颜色从 this.result 来,如果没有,则从 this.draw.getColorMatch 来
     *  初始化颜色配置时，默认以 theme 为基础，如果用户没有修改任何值，则 result 是没有值的，这时，取的就是 theme 的值
     *  如果修改了某一个值，则会全部读取 input 的值，生成新的 colorMatch 这时，result 会有全部的值
     */
    const defaultTheme = this.draw.getColorScheme().getColorMatch();
    const result = <IColorMatch>this.result || defaultTheme;

    const selector = ".content-preview";
    const canvasBox = <HTMLElement>this.dialog.querySelector(selector);
    const canvas = <HTMLCanvasElement>canvasBox?.querySelector("canvas");

    canvas.width = canvasBox.clientWidth;
    canvas.height = canvasBox.clientHeight;

    if (!canvas) return;

    const ctx = canvas.getContext("2d")!;

    if (!ctx) return;

    ctx.clearRect(0, 0, canvas.width, canvas.height);

    // 这个是 幻灯片 背景、边框颜色
    canvas.style.backgroundColor = <string>result.slideFillColor;
    canvas.style.border = `1px solid ${<string>result.slideStrokeColor}`;

    const rectW = 80;
    const rectH = 90;
    const marginTop = 20;
    const offset = 20;

    // 绘制矩形
    ctx.beginPath();
    ctx.rect(0, marginTop, rectW, rectH);
    ctx.fillStyle = <string>result.nodeFillColor; // 矩形填充
    ctx.fill();
    ctx.strokeStyle = <string>result.nodeStrokeColor; // 矩形边框
    ctx.stroke();
    ctx.closePath();

    // 绘制文本
    ctx.beginPath();
    ctx.fillStyle = <string>result.nodeTextColor; // 文字颜色
    ctx.font = "20px Arial";
    const textX = rectW / 2 - ctx.measureText("hello").width / 2;
    const textY = rectH / 2 + marginTop + 10;
    ctx.fillText("Hello", textX, textY);
    ctx.closePath();

    // 绘制形变节点矩形
    ctx.beginPath();
    ctx.rect(rectW + offset, marginTop, rectW, rectH);
    ctx.fillStyle = <string>result.nodeFillColor;
    ctx.fill();
    ctx.strokeStyle = <string>result.transformerColor;
    ctx.stroke();
    ctx.closePath();

    // 绘制形变节点控制点
    ctx.beginPath();
    // 所有点的集合
    const points = [
      { x: rectW + offset, y: marginTop },
      { x: rectW + offset + rectW / 2, y: marginTop },
      { x: rectW + offset + rectW, y: marginTop },
      { x: rectW + offset + rectW, y: marginTop + rectH / 2 },
      { x: rectW + offset + rectW, y: marginTop + rectH },
      { x: rectW + offset + rectW / 2, y: marginTop + rectH },
      { x: rectW + offset, y: marginTop + rectH },
      { x: rectW + offset, y: marginTop + rectH / 2 },
      { x: rectW + 20 + rectW / 2, y: 5 }, // 这个是旋转控制点
    ];
    ctx.fillStyle = <string>result.transformerColor; // 形变节点控制点颜色
    points.forEach(({ x, y }) => {
      ctx.moveTo(x, y);
      ctx.arc(x, y, 5, 0, 2 * Math.PI);
      ctx.fill();
    });
    // 旋转控制点直线
    ctx.moveTo(rectW + 20 + rectW / 2, 0);
    ctx.lineTo(rectW + 20 + rectW / 2, marginTop);
    ctx.stroke();
    ctx.closePath();

    ctx.beginPath();
    ctx.fillStyle = <string>result.nodeTextColor;
    ctx.font = "30px Arial";
    const px = rectW + offset + rectW / 2 - ctx.measureText("P").width / 2;
    const py = rectH / 2 + marginTop + 15;
    ctx.fillText("P", px, py);
    ctx.closePath();

    // 绘制表格
    const data = [
      ["表头一", "表头二"],
      ["测试数据", "测试数据"],
      ["测试数据", "测试数据"],
    ];
    const columnWidths = [70, 70];
    const rowHeight = 30;

    for (let i = 0; i < data.length; i++) {
      for (let j = 0; j < data[i].length; j++) {
        const cellX =
          j === 0
            ? rectW * 2 + offset * 2
            : columnWidths.reduce((prev, cur, idx) => {
                if (idx < j) return prev + cur;
                else return prev;
              }, rectW * 2 + offset * 2);

        const cellY = i * rowHeight + marginTop;
        const cellWidth = columnWidths[j];
        const cellHeight = rowHeight;

        // 绘制单元格边框 - 奇偶行实现斑马纹
        if (i) {
          ctx.beginPath();
          const table_stripeColor = result.tableStripeColor!;
          ctx.fillStyle = i % 2 ? table_stripeColor[0] : table_stripeColor[1];
          ctx.fillRect(cellX, cellY, cellWidth, cellHeight);
          ctx.closePath();
        } else {
          // 取表头颜色
          ctx.beginPath();
          const table_headerColor = result.tableHeaderFillColor!;
          ctx.fillStyle = table_headerColor;
          ctx.fillRect(cellX, cellY, cellWidth, cellHeight);
          ctx.closePath();
        }

        // 是否启用边框 - 边框颜色
        ctx.beginPath();
        ctx.strokeStyle = result.tableBorderColor!;
        ctx.strokeRect(cellX, cellY, cellWidth, cellHeight);
        ctx.closePath();

        // 绘制单元格内容;
        ctx.fillStyle = result.tableTextColor!; // 设置文本颜色为黑色
        ctx.font = "14px Arial"; // 设置字体样式
        ctx.fillText(data[i][j].toString(), cellX + 5, cellY + 20); // 绘制文本内容
      }
    }

    // 绘制矩形 - 5 个柱子即可
    const baseHeight = rectH + offset * 2;

    const seriesColor = <string[]>result.seriesColor; // 取颜色
    for (let i = 0; i < 5; i++) {
      ctx.beginPath();
      ctx.fillStyle = seriesColor[i];
      ctx.rect(
        offset * 3 * i + offset,
        baseHeight + (i % 3) * offset,
        rectW * 0.5,
        baseHeight - (i % 3) * offset
      );
      ctx.fill();
      ctx.closePath();
    }
  }

  // 幻灯片模板实现
  private initSlideTempEvent() {
    if (!this.dialog) return;

    const layerManager = this.draw.getLayerManager();
    const contentBox = this.dialog.querySelector(".content")!;
    // cover 封面页 directory 目录页 others 正文页 last 结束页 chapter 章节页
    const slideTempType = ["cover", "directory", "chapter", "last", "others"];

    // 定义模板事件
    const setSlideTemp = (index: number) => {
      // 获取 slideTemp
      const slideTempList = SLIDE_TEMPLATE.map((i) => i).filter(
        (i) => i.type === slideTempType[index]
      );

      contentBox.innerHTML = "";

      slideTempList.forEach((item) => {
        const image = document.createElement("img");
        image.src = item.image;
        contentBox.appendChild(image);

        image.addEventListener("click", () => {
          // 直接将json进行解析
          const layer = layerManager.parseLayerJson(item.data);
          if (layer) this.draw.addSlideToEditor(layer);
          // 关闭弹窗
          this.close(this.payload);
        });
      });
    };

    // 1. 初始化按钮事件
    const spans = this.dialog.querySelectorAll(".menu span");
    spans.forEach((span, index) => {
      span.addEventListener("click", () => {
        spans.forEach((span) => span.classList.remove("active"));
        span.classList.add("active");
        setSlideTemp(index);
      });
    });

    // 2. 先默认初始化 首页 setSlideTemp(index);
    setSlideTemp(0);
  }
}
